/*
 *  msw7/windowImp.cpp
 *  mini
 *
 *  Created by jin on 1/1/11.
 *  Copyright 2011 cs. All rights reserved.
 *
 */
#include "msw7/windowImp.h"

#include <tchar.h>
#include <math.h>

#include "window.h"
#include "mouseEvent.h"
#include "msw7/graphic2D.h"
#include "msw7/appImp.h"

static HINSTANCE hInstance = NULL; // TODO : Store instance handle in our global variable

inline void ParamToEvent(MouseEvent& evt, WPARAM wParam, LPARAM lParam)
{
    evt.x = HIWORD(lParam);
    evt.y = LOWORD(lParam);
    evt.modify = 0;
    if (wParam & 0x4)
        evt.modify |= SHIFT_DOWN;
    if (wParam & 0x8)
        evt.modify |= CTRL_DOWN;
}

LRESULT CALLBACK MSW7WindowImp::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    MSW7WindowImp *pImp = reinterpret_cast<MSW7WindowImp *>(static_cast<LONG_PTR>(
        ::GetWindowLongPtrW(
        hWnd,
        GWLP_USERDATA
        )));

    if (pImp == NULL)
    {
        return DefWindowProc(hWnd, message, wParam, lParam);
    }

    switch (message)
    {
        case WM_SIZE:
            pImp->HandleResize(wParam, lParam);
            break;
        case WM_MOUSEMOVE:
            pImp->HandleMouseMove(wParam, lParam);
            break;
        case WM_LBUTTONDOWN:
            pImp->HandleMouseLeftDown(wParam, lParam);
            break;
        case WM_LBUTTONUP:
            pImp->HandleMouseLeftUp(wParam, lParam);
            break;
        case WM_PAINT:
        case WM_DISPLAYCHANGE:
            pImp->HandlePaint(wParam, lParam);
            break;
        case WM_DESTROY:
            pImp->HandleDestory(wParam, lParam);;
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

bool MSW7WindowImp::HandleMouseLeftDown(WPARAM wParam, LPARAM lParam)
{
    MouseEvent evt;
    ParamToEvent(evt, wParam, lParam);
    owner->OnMouseDown(evt);
    return true;
}

bool MSW7WindowImp::HandleMouseMove(WPARAM wParam, LPARAM lParam)
{
    MouseEvent evt;
    ParamToEvent(evt, wParam, lParam);
    owner->OnMouseMove(evt);
    return true;
}

bool MSW7WindowImp::HandleMouseLeftUp(WPARAM wParam, LPARAM lParam)
{
    MouseEvent evt;
    ParamToEvent(evt, wParam, lParam);
    owner->OnMouseUp(evt);
    return true;
}

bool MSW7WindowImp::HandlePaint(WPARAM wParam, LPARAM lParam)
{
    OnRender();
    return true;
}

bool MSW7WindowImp::HandleDestory(WPARAM wParam, LPARAM lParam)
{
    delete owner;
    return true;
}

bool MSW7WindowImp::HandleResize(WPARAM wParam, LPARAM lParam)
{
    UINT width = LOWORD(lParam);
    UINT height = HIWORD(lParam);
    OnResize(width, height);
    return true;
}

void MSW7WindowImp::OnResize(unsigned int width, unsigned int height)
{
    if (m_pGraphic)
        m_pGraphic->Resize(width, height);
    owner->OnResize(width, height);
}


ATOM MSW7WindowImp::RegisterWindowClass()
{
    static bool registered = false;
    if (registered)
        return 0;

    registered = true;

    WNDCLASSEX wcex;

    wcex.cbSize = sizeof (WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = MSW7WindowImp::WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = sizeof(LONG_PTR);
    wcex.hInstance = hInstance;
    wcex.hIcon = NULL;
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    wcex.lpszMenuName = _T("");
    wcex.lpszClassName = _T("MiniWindow");
    wcex.hIconSm = NULL;

    return RegisterClassEx(&wcex);
}

MSW7WindowImp::MSW7WindowImp(Window* owner_) : WindowImpBase(owner_), m_pGraphic(NULL)
{
    RegisterWindowClass();

    FLOAT dpiX, dpiY;
    MSW7Graphic2D::pD2DFactory->GetDesktopDpi(&dpiX, &dpiY);

    hwd = CreateWindow(
        L"MiniWindow",
        L"mini",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        static_cast<UINT>(ceil(640.f * dpiX / 96.f)),
        static_cast<UINT>(ceil(480.f * dpiY / 96.f)),
        NULL,
        NULL,
        hInstance,
        this
        );

    if (hwd)
    {
        m_pGraphic = new MSW7Graphic2D;

        ::SetWindowLongPtrW(
            hwd,
            GWLP_USERDATA,
            PtrToUlong(this)
            );
    }
}

MSW7WindowImp::~MSW7WindowImp()
{
    delete m_pGraphic;
    m_pGraphic = NULL;
}

void* MSW7WindowImp::GetHandle()
{
    return hwd;
}

void MSW7WindowImp::Show(bool show)
{
    ShowWindow(hwd, show ? SW_SHOWNORMAL : SW_HIDE);
    UpdateWindow(hwd);
}

HRESULT MSW7WindowImp::OnRender()
{
    HRESULT hr;
    RECT rc;

    // Get the dimensions of the client drawing area.
    GetClientRect(hwd, &rc);

    // Create the DC render target.
    hr = m_pGraphic->CreateDeviceResources(hwd);

    if (SUCCEEDED(hr))
    {
        PAINTSTRUCT ps;
        BeginPaint(hwd, &ps);

        // Bind the DC to the DC render target.
        //hr = m_pGraphic->Bind(ps.hdc, rc);

        m_pGraphic->BeginDraw();
        owner->OnPaint(*m_pGraphic);
        hr = m_pGraphic->EndDraw();

        EndPaint(hwd, &ps);
    }

    if (hr == D2DERR_RECREATE_TARGET)
    {
        hr = S_OK;
        m_pGraphic->DiscardDeviceResources();
    }

    return hr;
}